#include "MineSweeperEnvironment.h"
#include "../../Exception/GA_Exception.h"
#include "../../Crossover/SinglePoint/SinglePointCrossover.h"
#include <cstdlib>
#include <sstream>
#include <ostream>

MineSweeperEnvironment::MineSweeperEnvironment()
{
}

MineSweeperEnvironment::~MineSweeperEnvironment()
{
}

Environment *MineSweeperEnvironment::getEnvironment()
{
    return new MineSweeperEnvironment();
}

Crossover *MineSweeperEnvironment::configureCrossover()
{
    return new SinglePointCrossover<ValueEncodedSolution2d<bool> >();
}

void MineSweeperEnvironment::configure()
{
    int rows;
    int columns;
    cout << "How many rows should the mine field have? ";
    cin >> rows;
    cout << "How many columns should the mine field have? ";
    cin >> columns;
    
    generateAdjacencyTable(rows, columns);
    outputTable();
}

void MineSweeperEnvironment::outputTable()
{
    cout << "Adjacency Table:" << endl;
    for (unsigned int i = 0; i < adjacencyTable.size(); ++i)
    {
        for (unsigned int j = 0; j < adjacencyTable[i].size(); ++j)
        {
            cout << adjacencyTable[i][j] << ' ';
        }
        cout << endl;
    }
}

void MineSweeperEnvironment::generateAdjacencyTable(int rows, int columns)
{
    adjacencyTable.clear();
    for (int i = 0; i < rows; ++i)
    {
        vector<int> newRow;
        adjacencyTable.push_back(newRow);
        for (int j = 0; j < columns; ++j)
        {
            if (MyRandom::randInt(0, 1) == 0)
                adjacencyTable[i].push_back(0);
            else
                adjacencyTable[i].push_back(MyRandom::randInt(0, 8));
        }
    }
}

void MineSweeperEnvironment::configure(string filename)
{
}

double MineSweeperEnvironment::evaluateFitness(Solution *sol)
{
    ValueEncodedSolution2d<bool> *solution = (ValueEncodedSolution2d<bool>*)sol;
    vector<vector<bool> > chromosome = solution->getSolution();
    
    if (chromosome.size() != adjacencyTable.size())
        throw Invalid_solution_size_exception();
        
    int incorrectnessCount = getIncorrectnessCount(chromosome);
    
    if (incorrectnessCount == 0)
        return LONG_MAX;
        
    return 100.0 / incorrectnessCount;
}

int MineSweeperEnvironment::getIncorrectnessCount(vector<vector<bool> > chromosome)
{
    int incorrectnessCount = 0;
    
    unsigned int size = chromosome.size();
    for (unsigned int i = 0; i < size; ++i)
    {
        unsigned int size2 = chromosome[i].size();
        if (size2 != adjacencyTable[i].size())
            throw Invalid_solution_size_exception();
            
        for (unsigned int j = 0; j < size2; ++j) 
        {
            if (chromosome[i][j] && adjacencyTable[i][j] != 0)
                incorrectnessCount += 9;
            else if (!(chromosome[i][j] && adjacencyTable[i][j] == 0))
                incorrectnessCount += abs(getAdjacentMineCount(i, j, chromosome) - adjacencyTable[i][j]);
        }
    }
    return incorrectnessCount;
}

int MineSweeperEnvironment::getAdjacentMineCount(unsigned int row, unsigned int column, vector<vector<bool> > mineField)
{
    int mineCount = 0;
    if (mineField.size() < row)
        throw Invalid_index_exception();
    
    try
    {
	    if (mineField.at(row - 1).at(column - 1))
	        ++mineCount;
    } catch (std::exception &e) { /* don't care */ }
    
    try
    {
	    if (mineField.at(row - 1).at(column))
	        ++mineCount;
    } catch (std::exception &e) { /* don't care */ }

    try
    {    
	    if (mineField.at(row - 1).at(column + 1))
	        ++mineCount;
    } catch (std::exception &e) { /* don't care */ }
    
    try
    {
	    if (mineField.at(row).at(column - 1))
	        ++mineCount;
    } catch (std::exception &e) { /* don't care */}
    
    try
    {
	    if (mineField.at(row).at(column + 1))
	        ++mineCount;
    } catch (std::exception &e) { /* don't care */ }
    
    try
    {
	    if (mineField.at(row + 1).at(column - 1))
	        ++mineCount;            
    } catch (std::exception &e) { /* don't care */ }
    
    try
    {
	    if (mineField.at(row + 1).at(column))
	        ++mineCount;        
    } catch (std::exception &e) { /* don't care */ }
    
    try
    {
	    if (mineField.at(row + 1).at(column + 1))
	        ++mineCount;
    } catch (std::exception &e) { /* don't care */ }

    return mineCount;            
}

void MineSweeperEnvironment::generateRandomPopulation(vector<Solution*> &solutions, unsigned int size)
{
    for (unsigned int i = 0; i < size; ++i)
    {
        vector<vector<bool> > randChromosome;
        for (unsigned int j = 0; j < adjacencyTable.size(); ++j)
        {
            vector<bool> newRow;
            randChromosome.push_back(newRow);
            for (unsigned int k = 0; k < adjacencyTable[j].size(); ++k)
            {
                randChromosome[j].push_back(MyRandom::randInt(0, 1));
            }
        }
        solutions.push_back(new ValueEncodedSolution2d<bool>(randChromosome));
    }
}

string MineSweeperEnvironment::formatSolution(Solution *sol)
{    
    ValueEncodedSolution2d<bool>* solution = (ValueEncodedSolution2d<bool>*)sol;
    ostringstream returnMe;
    returnMe << "\n" << sol;
    returnMe << "         - off by: " << getIncorrectnessCount(solution->getSolution());
    return returnMe.str();
}

string MineSweeperEnvironment::getName()
{
    return "Mine Sweeper";
}

